package pub.tbc.api.invoker.protocol.http.netty;

import com.google.common.base.Charsets;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;
import pub.tbc.api.invoker.protocol.http.AbstractHttpInvoker;
import pub.tbc.api.invoker.protocol.http.NettyHttpUtil;

/**
 * @author tbc on 2017/3/10 09:22:46.
 */
@Slf4j
public class DefaultNettyHttpClient extends AbstractHttpInvoker {


    @Override
    public void invoke(ChannelHandlerContext context, FullHttpRequest request) {
        String host = "it";
        int port = 8888;

        Channel inboundChannel = context.channel();

        Bootstrap b = new Bootstrap();
        b.group(NettyHttpUtil.getClientEventLoopGroup())     // use inboundChannel thread
                .channel(context.channel().getClass())
                .handler(new BackendHandlerInitializer(inboundChannel));

        ChannelFuture f = b.connect(host, port);
        Channel outboundChannel = f.channel();
        request.retain();// 当客户端代码中需要保持一个该对象的引用时，调用此方法将计数加1
        ChannelFuture channelFuture = f.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    FullHttpRequest req = new DefaultFullHttpRequest(
                            HttpVersion.HTTP_1_1,
                            request.method(),
//                            "/yijiajiao-wares/basic/subjects"
                            request.uri()
                    );
                    HttpHeaders reqHeaders = req.headers();
                    request.headers().forEach(header -> {
                        if (header.getKey().equalsIgnoreCase("host"))
//                            req.headers().set("Host", "211.157.179.221:22100");
                            req.headers().set("Host", "127.0.0.1:8888");
                        else
                            req.headers().set(header.getKey(), header.getValue());
                    });
                    log.debug("send request : {}", req);

                    outboundChannel.writeAndFlush(req);
                } else {
                    inboundChannel.close();
                }
            }
        });
    }


    private static class BackendHandlerInitializer extends ChannelInitializer<SocketChannel> {

        final Channel inboundChannel;

        public BackendHandlerInitializer(Channel inboundChannel) {
            this.inboundChannel = inboundChannel;
        }

        @Override
        public void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new HttpClientCodec())
                    .addLast(new HttpObjectAggregator(1024 * 1024))
                    .addLast(new HttpBackendHandler(inboundChannel));
        }
    }

    private static class HttpBackendHandler extends SimpleChannelInboundHandler<FullHttpResponse> {

        private final Channel inboundChannel;

        public HttpBackendHandler(Channel inboundChannel) {
            this.inboundChannel = inboundChannel;
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("[转发连接]Backend Handler is Active!");
            super.channelActive(ctx);
        }

        @Override
        public void channelRead0(final ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
            log.info("收到第三方响应: {}", msg);
            log.debug("{}", msg.content().toString(Charsets.UTF_8));

            ChannelFuture channelFuture = inboundChannel.writeAndFlush(msg.retain());
            channelFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    log.debug(".......................................");
                    if (!future.isSuccess()) {
                        future.channel().close().sync();
                    }
                }
            });
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            System.out.println("[代理连接断开]Backend Handler destroyed!");
            NettyHttpUtil.closeOnFlush(inboundChannel);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            NettyHttpUtil.closeOnFlush(ctx.channel());
        }
    }
}
